package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.activity.client.ActivityFeignClient;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.model.activity.ActivityRule;
import com.atguigu.gmall.model.activity.CouponInfo;
import com.atguigu.gmall.model.enums.ActivityType;
import com.atguigu.gmall.model.enums.CouponType;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.*;
import com.atguigu.gmall.order.mapper.*;
import com.atguigu.gmall.order.service.OrderService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author ：Shine
 * @description：
 * @date ：2021/3/3 18:05
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderService {
    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private OrderDetailCouponMapper orderDetailCouponMapper;

    @Autowired
    private OrderDetailActivityMapper orderDetailActivityMapper;

    @Autowired
    private ActivityFeignClient activityFeignClient;

    @Autowired
    private OrderStatusLogMapper orderStatusLogMapper;

    @Value("${ware.url}")
    private String wareUrl;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveOrderInfo(OrderInfo orderInfo) {
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        String outTradeNo = "ATGUIGU" + System.currentTimeMillis() + "" + new Random().nextInt(1000);
        orderInfo.setOutTradeNo(outTradeNo);
        orderInfo.setCreateTime(new Date());
        // 定义为1天
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, 1);
        orderInfo.setExpireTime(calendar.getTime());
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());

        //订单明细
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        StringBuffer tradeBody = new StringBuffer();
        for (OrderDetail orderDetail : orderDetailList) {
            tradeBody.append(orderDetail.getSkuName() + " ");
        }
        if (tradeBody.length() > 100) {
            orderInfo.setTradeBody(tradeBody.toString().substring(0, 100));
        } else {
            orderInfo.setTradeBody(tradeBody.toString());
        }
        orderInfo.setFeightFee(new BigDecimal("0"));
        orderInfo.setCreateTime(new Date());
        orderInfo.setOperateTime(orderInfo.getCreateTime());

        //促销优惠总金额
        BigDecimal activityReduceAmount = this.getActivityReduceAmount(orderInfo);
        orderInfo.setActivityReduceAmount(activityReduceAmount);

        orderInfo.sumTotalAmount();
        orderInfoMapper.insert(orderInfo);

        //计算购物项分摊的优惠减少金额，按比例分摊，退款时按实际支付金额退款
        Map<String, BigDecimal> skuIdToReduceAmountMap = this.computeOrderDetailPayAmount(orderInfo);
        //sku对应的订单明细
        Map<Long, Long> skuIdToOrderDetailIdMap = new HashMap<>();
        // 保存订单明细
        for (OrderDetail orderDetail : orderDetailList) {
            orderDetail.setOrderId(orderInfo.getId());
            orderDetail.setCreateTime(new Date());

            //促销活动分摊金额
            BigDecimal splitActivityAmount = skuIdToReduceAmountMap.get("activity:" + orderDetail.getSkuId());
            if (null == splitActivityAmount) {
                splitActivityAmount = new BigDecimal(0);
            }
            orderDetail.setSplitActivityAmount(splitActivityAmount);

            //优惠券分摊金额
            BigDecimal splitCouponAmount = skuIdToReduceAmountMap.get("coupon:" + orderDetail.getSkuId());
            if (null == splitCouponAmount) {
                splitCouponAmount = new BigDecimal(0);
            }
            orderDetail.setSplitCouponAmount(splitCouponAmount);

            //优惠后的总金额
            BigDecimal skuTotalAmount = orderDetail.getOrderPrice().multiply(new BigDecimal(orderDetail.getSkuNum()));
            BigDecimal payAmount = skuTotalAmount.subtract(splitActivityAmount).subtract(splitCouponAmount);
            orderDetail.setSplitTotalAmount(payAmount);

            orderDetailMapper.insert(orderDetail);

            skuIdToOrderDetailIdMap.put(orderDetail.getSkuId(), orderDetail.getId());
        }

        //记录订单与促销活动和优惠券的关联信息
        this.saveActivityAndCouponRecord(orderInfo, skuIdToOrderDetailIdMap);

        //记录订单状态
        this.saveOrderStatusLog(orderInfo.getId(), orderInfo.getOrderStatus());

        //发送延迟队列，如果定时未支付，取消订单
        rabbitService.sendDelayMessage(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL, MqConst.ROUTING_ORDER_CANCEL, orderInfo.getId(), MqConst.DELAY_TIME);
        // 返回
        return orderInfo.getId();

    }

    /**
     * 计算购物项分摊的优惠减少金额
     * 打折：按折扣分担
     * 现金：按比例分摊
     *
     * @param orderInfo
     * @return
     */
    private Map<String, BigDecimal> computeOrderDetailPayAmount(OrderInfo orderInfo) {
        Map<String, BigDecimal> skuIdToReduceAmountMap = new HashMap<>();
        List<OrderDetailVo> orderDetailVoList = orderInfo.getOrderDetailVoList();
        if (!CollectionUtils.isEmpty(orderDetailVoList)) {
            for (OrderDetailVo orderDetailVo : orderDetailVoList) {
                ActivityRule activityRule = orderDetailVo.getActivityRule();
                List<OrderDetail> orderDetailList = orderDetailVo.getOrderDetailList();
                if (null != activityRule) {
                    //优惠金额， 按比例分摊
                    BigDecimal reduceAmount = activityRule.getReduceAmount();
                    if (orderDetailList.size() == 1) {
                        skuIdToReduceAmountMap.put("activity:" + orderDetailList.get(0).getSkuId(), reduceAmount);
                    } else {
                        //总金额
                        BigDecimal originalTotalAmount = new BigDecimal(0);
                        for (OrderDetail orderDetail : orderDetailList) {
                            BigDecimal skuTotalAmount = orderDetail.getOrderPrice().multiply(new BigDecimal(orderDetail.getSkuNum()));
                            originalTotalAmount = originalTotalAmount.add(skuTotalAmount);
                        }
                        //记录除最后一项是所有分摊金额， 最后一项=总的 - skuPartReduceAmount
                        BigDecimal skuPartReduceAmount = new BigDecimal(0);
                        if (activityRule.getActivityType().equals(ActivityType.FULL_REDUCTION.name())) {
                            for (int i = 0, len = orderDetailList.size(); i < len; i++) {
                                OrderDetail orderDetail = orderDetailList.get(i);
                                if (i < len - 1) {
                                    BigDecimal skuTotalAmount = orderDetail.getOrderPrice().multiply(new BigDecimal(orderDetail.getSkuNum()));
                                    //sku分摊金额
                                    BigDecimal skuReduceAmount = skuTotalAmount.divide(originalTotalAmount, 2, RoundingMode.HALF_UP).multiply(reduceAmount);
                                    skuIdToReduceAmountMap.put("activity:" + orderDetail.getSkuId(), skuReduceAmount);

                                    skuPartReduceAmount = skuPartReduceAmount.add(skuReduceAmount);
                                } else {
                                    BigDecimal skuReduceAmount = reduceAmount.subtract(skuPartReduceAmount);
                                    skuIdToReduceAmountMap.put("activity:" + orderDetail.getSkuId(), skuReduceAmount);
                                }
                            }
                        } else {
                            for (int i = 0, len = orderDetailList.size(); i < len; i++) {
                                OrderDetail orderDetail = orderDetailList.get(i);
                                if (i < len - 1) {
                                    BigDecimal skuTotalAmount = orderDetail.getOrderPrice().multiply(new BigDecimal(orderDetail.getSkuNum()));

                                    //sku分摊金额
                                    BigDecimal skuDiscountTotalAmount = skuTotalAmount.multiply(activityRule.getBenefitDiscount().divide(new BigDecimal("10")));
                                    BigDecimal skuReduceAmount = skuTotalAmount.subtract(skuDiscountTotalAmount);
                                    skuIdToReduceAmountMap.put("activity:" + orderDetail.getSkuId(), skuReduceAmount);

                                    skuPartReduceAmount = skuPartReduceAmount.add(skuReduceAmount);
                                } else {
                                    BigDecimal skuReduceAmount = reduceAmount.subtract(skuPartReduceAmount);
                                    skuIdToReduceAmountMap.put("activity:" + orderDetail.getSkuId(), skuReduceAmount);
                                }
                            }
                        }
                    }
                }
            }
        }

        CouponInfo couponInfo = orderInfo.getCouponInfo();
        if (null != couponInfo) {
            //sku对应的订单明细
            Map<Long, OrderDetail> skuIdToOrderDetailMap = new HashMap<>();
            for (OrderDetail orderDetail : orderInfo.getOrderDetailList()) {
                skuIdToOrderDetailMap.put(orderDetail.getSkuId(), orderDetail);
            }
            //优惠券对应的skuId列表
            List<Long> skuIdList = couponInfo.getSkuIdList();
            //优惠券优化总金额
            BigDecimal reduceAmount = couponInfo.getReduceAmount();
            if (skuIdList.size() == 1) {
                //sku总的优化金额 = 促销 + 优惠券
                BigDecimal skuActicityReduceAmount = skuIdToReduceAmountMap.get("activity:" + skuIdList.get(0));
                if (null == skuActicityReduceAmount) {
                    skuActicityReduceAmount = new BigDecimal(0);
                }
                skuIdToReduceAmountMap.put("coupon:" + skuIdToOrderDetailMap.get(skuIdList.get(0)).getSkuId(), reduceAmount);
            } else {
                //总金额
                BigDecimal originalTotalAmount = new BigDecimal(0);
                for (Long skuId : skuIdList) {
                    OrderDetail orderDetail = skuIdToOrderDetailMap.get(skuId);
                    BigDecimal skuTotalAmount = orderDetail.getOrderPrice().multiply(new BigDecimal(orderDetail.getSkuNum()));
                    originalTotalAmount = originalTotalAmount.add(skuTotalAmount);
                }
                //记录除最后一项是所有分摊金额， 最后一项=总的 - skuPartReduceAmount
                BigDecimal skuPartReduceAmount = new BigDecimal(0);
                if (couponInfo.getCouponType().equals(CouponType.CASH.name()) || couponInfo.getCouponType().equals(CouponType.FULL_REDUCTION.name())) {
                    for (int i = 0, len = skuIdList.size(); i < len; i++) {
                        OrderDetail orderDetail = skuIdToOrderDetailMap.get(skuIdList.get(i));
                        if (i < len - 1) {
                            BigDecimal skuTotalAmount = orderDetail.getOrderPrice().multiply(new BigDecimal(orderDetail.getSkuNum()));
                            //sku分摊金额
                            BigDecimal skuReduceAmount = skuTotalAmount.divide(originalTotalAmount, 2, RoundingMode.HALF_UP).multiply(reduceAmount);
                            skuIdToReduceAmountMap.put("coupon:" + orderDetail.getSkuId(), skuReduceAmount);

                            skuPartReduceAmount = skuPartReduceAmount.add(skuReduceAmount);
                        } else {
                            BigDecimal skuReduceAmount = reduceAmount.subtract(skuPartReduceAmount);
                            skuIdToReduceAmountMap.put("coupon:" + orderDetail.getSkuId(), skuReduceAmount);
                        }
                    }
                } else {
                    for (int i = 0, len = skuIdList.size(); i < len; i++) {
                        OrderDetail orderDetail = skuIdToOrderDetailMap.get(skuIdList.get(i));
                        if (i < len - 1) {
                            BigDecimal skuTotalAmount = orderDetail.getOrderPrice().multiply(new BigDecimal(orderDetail.getSkuNum()));
                            BigDecimal skuDiscountTotalAmount = skuTotalAmount.multiply(couponInfo.getBenefitDiscount().divide(new BigDecimal("10")));
                            BigDecimal skuReduceAmount = skuTotalAmount.subtract(skuDiscountTotalAmount);
                            //sku分摊金额
                            skuIdToReduceAmountMap.put("coupon:" + orderDetail.getSkuId(), skuReduceAmount);

                            skuPartReduceAmount = skuPartReduceAmount.add(skuReduceAmount);
                        } else {
                            BigDecimal skuReduceAmount = reduceAmount.subtract(skuPartReduceAmount);
                            skuIdToReduceAmountMap.put("coupon:" + orderDetail.getSkuId(), skuReduceAmount);
                        }
                    }
                }
            }
        }
        return skuIdToReduceAmountMap;
    }


    private void saveActivityAndCouponRecord(OrderInfo orderInfo, Map<Long, Long> skuIdToOrderDetailIdMap) {
        //记录促销活动
        List<OrderDetailVo> orderDetailVoList = orderInfo.getOrderDetailVoList();
        if (!CollectionUtils.isEmpty(orderDetailVoList)) {
            for (OrderDetailVo orderDetailVo : orderDetailVoList) {
                ActivityRule activityRule = orderDetailVo.getActivityRule();
                if (null != activityRule) {
                    for (Long skuId : activityRule.getSkuIdList()) {
                        OrderDetailActivity orderDetailActivity = new OrderDetailActivity();
                        orderDetailActivity.setOrderId(orderInfo.getId());
                        orderDetailActivity.setOrderDetailId(skuIdToOrderDetailIdMap.get(skuId));
                        orderDetailActivity.setActivityId(activityRule.getActivityId());
                        orderDetailActivity.setActivityRule(activityRule.getId());
                        orderDetailActivity.setSkuId(skuId);
                        orderDetailActivity.setCreateTime(new Date());
                        orderDetailActivityMapper.insert(orderDetailActivity);
                    }
                }
            }
        }

        //记录优惠券
        //是否更新优惠券状态
        Boolean isUpdateCouponStatus = false;
        CouponInfo couponInfo = orderInfo.getCouponInfo();
        if (null != couponInfo) {
            List<Long> skuIdList = couponInfo.getSkuIdList();
            for (Long skuId : skuIdList) {
                OrderDetailCoupon orderDetailCoupon = new OrderDetailCoupon();
                orderDetailCoupon.setOrderId(orderInfo.getId());
                orderDetailCoupon.setOrderDetailId(skuIdToOrderDetailIdMap.get(skuId));
                orderDetailCoupon.setCouponId(couponInfo.getId());
                orderDetailCoupon.setSkuId(skuId);
                orderDetailCoupon.setCreateTime(new Date());
                orderDetailCouponMapper.insert(orderDetailCoupon);

                //更新优惠券使用状态
                if (!isUpdateCouponStatus) {
                    activityFeignClient.updateCouponInfoUseStatus(couponInfo.getId(), orderInfo.getUserId(), orderInfo.getId());
                }
                isUpdateCouponStatus = true;
            }
        }


    }

    private BigDecimal getActivityReduceAmount(OrderInfo orderInfo) {
        //促销优惠金额
        BigDecimal activityReduceAmount = new BigDecimal("0");
        List<OrderDetailVo> orderDetailVoList = orderInfo.getOrderDetailVoList();
        if (!CollectionUtils.isEmpty(orderDetailVoList)) {
            for (OrderDetailVo orderDetailVo : orderDetailVoList) {
                ActivityRule activityRule = orderDetailVo.getActivityRule();
                if (null != activityRule) {
                    activityReduceAmount = activityReduceAmount.add(activityRule.getReduceAmount());
                }
            }
        }
        return activityReduceAmount;

    }

    @Override
    public String getTradeNo(String userId) {
        String tradeNo = UUID.randomUUID().toString().replaceAll("-", "");

        //将数据存储到缓存中
        String tradeNoKey = "user:" + userId + ":tradeNo";
        redisTemplate.opsForValue().set(tradeNoKey, tradeNo);
        return tradeNo;
    }

    @Override
    public boolean checkTradeNo(String tradeNO, String userId) {
        String tradeNoKey = "user:" + userId + ":tradeNo";
        String redisTradeNo = (String) redisTemplate.opsForValue().get(tradeNoKey);
        return tradeNO.equals(redisTradeNo);
    }

    @Override
    public void deleteTradeNo(String userId) {
        String tradeNoKey = "user:" + userId + ":tradeNo";
        redisTemplate.delete(tradeNoKey);
    }

    @Override
    public boolean checkStock(Long skuId, Integer skuNum) {
        //ware项目只是单纯的springBoot项目，不是springBoot+SpringCloud项目，不可以使用feign进行调用，使用最原始的HttpClient进行操作
        String result = HttpClientUtil.doGet(wareUrl + "/hasStock?skuId=" + skuId + "&num=" + skuNum);
        return "1".equals(result);
    }

    @Override
    public void execExpiredOrder(Long orderId) {
        updateOrderStatus(orderId, ProcessStatus.CLOSED);

        //    如果有交易记录，我们需要关闭交易
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE, MqConst.ROUTING_PAYMENT_CLOSE, orderId);

    }

    @Override
    public void execExpiredOrder(Long orderId, String flag) {
        updateOrderStatus(orderId, ProcessStatus.CLOSED);
        if ("2".equals(flag)) {
            //    需要关闭两张表
            //OrderInfo表一定会是关闭的，如果是2，则paymentInfo表也需要发消息进行关闭
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE, MqConst.ROUTING_PAYMENT_CLOSE, orderId);
        }
    }

    //根据订单id进度转台更新订单表
    @Override
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
        orderInfo.setProcessStatus(processStatus.name());
        orderInfoMapper.updateById(orderInfo);
    }

    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        if (orderInfo != null) {
            List<OrderDetail> orderDetailList = orderDetailMapper.selectList(new QueryWrapper<OrderDetail>().eq("order_id", orderId));
            orderInfo.setOrderDetailList(orderDetailList);
        }
        return orderInfo;
    }

    @Override
    public void sendOrderStatus(Long orderId) {
        //    更新订单状态
        this.updateOrderStatus(orderId, ProcessStatus.NOTIFIED_WARE);

        String wearJson = this.initWareOrder(orderId);
        this.rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_WARE_STOCK, MqConst.ROUTING_WARE_STOCK, wearJson);
    }

    private String initWareOrder(Long orderId) {
        OrderInfo orderInfo = this.getOrderInfo(orderId);
        Map map = this.initWareOrder(orderInfo);
        return JSON.toJSONString(map);
    }

    @Override
    public Map initWareOrder(OrderInfo orderInfo) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("orderId", orderInfo.getId());
        map.put("consignee", orderInfo.getConsignee());
        map.put("consigneeTel", orderInfo.getConsigneeTel());
        map.put("orderComment", orderInfo.getOrderComment());
        map.put("orderBody", orderInfo.getTradeBody());
        map.put("deliveryAddress", orderInfo.getDeliveryAddress());
        map.put("paymentWay", "2");
        map.put("wareId", orderInfo.getWareId());

        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        ArrayList<Map<String, Object>> mapArrayList = new ArrayList<>();

        if (!CollectionUtils.isEmpty(orderDetailList)) {
            for (OrderDetail orderDetail : orderDetailList) {
                HashMap<String, Object> orderDetailMap = new HashMap<>();
                orderDetailMap.put("skuId", orderDetail.getSkuId());
                orderDetailMap.put("skuNum", orderDetail.getSkuNum());
                orderDetailMap.put("skuName", orderDetail.getSkuName());
                mapArrayList.add(orderDetailMap);
            }
        }
        map.put("details", mapArrayList);
        return map;
    }

    @Override
    public List<OrderInfo> orderSplit(Long orderId, String wareSkuMap) {
        List<OrderInfo> subOrderInfoArrayList = new ArrayList<>();

        //获取原始订单
        OrderInfo orderInfoOrigin = this.getOrderInfo(orderId);

        List<Map> mapList = JSON.parseArray(wareSkuMap, Map.class);
        if (!CollectionUtils.isEmpty(mapList)) {
            for (Map map : mapList) {
                String wareId = (String) map.get("wareId");
                List<String> skuIds = (List<String>) map.get("skuIds");

                //创建新的子订单
                OrderInfo subOrderInfo = new OrderInfo();
                //属性拷贝
                BeanUtils.copyProperties(orderInfoOrigin, subOrderInfo);
                subOrderInfo.setId(null);
                subOrderInfo.setParentOrderId(orderId);
                subOrderInfo.setWareId(wareId);

                List<OrderDetail> orderDetailArrayList = new ArrayList<>();
                //    获取到原始订单明细，更改新子订单明细状态
                List<OrderDetail> orderDetailList = orderInfoOrigin.getOrderDetailList();
                if (!CollectionUtils.isEmpty(orderDetailList)) {
                    for (OrderDetail orderDetail : orderDetailList) {

                        for (String skuId : skuIds) {
                            if (Long.parseLong(skuId) == orderDetail.getSkuId().intValue()) {
                                //将订单详情加入到集合中
                                orderDetailArrayList.add(orderDetail);
                            }
                        }
                    }
                }
                subOrderInfo.setOrderDetailList(orderDetailArrayList);
                //计算子订单新价格
                subOrderInfo.sumTotalAmount();

                // 将新子订单加入到集合中
                subOrderInfoArrayList.add(subOrderInfo);

                //保存子订单
                saveOrderInfo(subOrderInfo);
            }
            //修改原始订单状态
            this.updateOrderStatus(orderId, ProcessStatus.SPLIT);
        }

        return subOrderInfoArrayList;
    }

    @Override
    public void saveOrderStatusLog(Long orderId, String orderStatus) {
        //记录订单状态
        OrderStatusLog orderStatusLog = new OrderStatusLog();
        orderStatusLog.setOrderId(orderId);
        orderStatusLog.setOrderStatus(orderStatus);
        orderStatusLog.setOperateTime(new Date());
        orderStatusLogMapper.insert(orderStatusLog);
    }
}
